package com.cloudeasy.area.service;

import com.cloudeasy.api.constant.AmqpConstant;
import com.cloudeasy.api.enums.ResultCodeEnum;
import com.cloudeasy.api.exception.CloudEasyException;
import com.cloudeasy.api.util.JsonUtil;
import com.cloudeasy.api.vo.area.AreaRelyTreeVO;
import com.cloudeasy.api.vo.Result;
import com.cloudeasy.api.vo.amqp.ChangeOperateAreaVO;
import com.cloudeasy.api.vo.user.UserRoleVO;
import com.cloudeasy.area.client.UserClient;
import com.cloudeasy.area.constant.AreaConstant;
import com.cloudeasy.area.po.Area;
import com.cloudeasy.area.po.AreaRely;
import com.cloudeasy.area.repository.AreaRelyRepository;
import com.cloudeasy.area.repository.AreaRepository;
import com.cloudeasy.area.util.AreaPathUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class AreaRelyService {

    private final AreaRepository areaRepository;

    private final AreaRelyRepository areaRelyRepository;

    private final AmqpTemplate amqpTemplate;

    private final UserClient userClient;

    public AreaRelyService(AreaRepository areaRepository, AreaRelyRepository areaRelyRepository, AmqpTemplate amqpTemplate, UserClient userClient) {
        this.areaRepository = areaRepository;
        this.areaRelyRepository = areaRelyRepository;
        this.amqpTemplate = amqpTemplate;
        this.userClient = userClient;
    }

    /**
     * 存储节点信息
     * 若节点曾属于其它子节点，其子节点一并跟随迁移
     *
     * @param parentId 父节点
     * @param childId  子节点
     * @return {@link AreaRely}
     */
    public AreaRely save(String parentId, String childId) {
        if (childId.equals(AreaConstant.GLOBAL_ROOT_AREA_ID)) {
            throw new CloudEasyException(ResultCodeEnum.FORBID_UPDATE);
        }
        AreaRely childAreaRely = getAreaRely(childId);
        AreaRelyTreeVO tree = tree(childId);
        // 防止将父节点插入到自己的子节点中
        containsChildTree(tree, parentId);

        AreaRely parentAreaRely = null;
        if (parentId != null) {
            Optional<AreaRely> parentAreaRelyOptional = areaRelyRepository.findById(parentId);
            if (!parentAreaRelyOptional.isPresent()) {
                throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
            }
            parentAreaRely = parentAreaRelyOptional.get();
            if (parentAreaRely.getChildIds() != null) {
                if (parentAreaRely.getChildIds().contains(childId)) {
                    throw new CloudEasyException(ResultCodeEnum.REPEAT_ERROR);
                }
            }
        }
        // 如果子节点曾经有父节点，从父节点中删除掉子节点的信息
        String parentName = AreaPathUtil.parseParent(childAreaRely.getPath());
        if (parentName != null) {
            Optional<AreaRely> parentAreaRelyOptional = areaRelyRepository.findByNameLike(parentName);
            parentAreaRelyOptional.ifPresent(areaRely -> {
                areaRely.getChildIds().remove(childAreaRely.getId());
                areaRelyRepository.save(areaRely);
            });
        }

        // 如果插入的不是根节点
        if (parentAreaRely != null) {
            // 延伸路径
            childAreaRely.setPath(String.format("%s%s,", parentAreaRely.getPath(), childAreaRely.getName()));
            // 添加子区域Id
            List<String> childIds = parentAreaRely.getChildIds();
            // 如果之前为空则实例化
            if (parentAreaRely.getChildIds() == null) {
                childIds = new ArrayList<>();
                parentAreaRely.setChildIds(childIds);
            }
            // 将子区域的Id填入
            childIds.add(childAreaRely.getId());
            areaRelyRepository.save(parentAreaRely);
        } else {
            childAreaRely.setPath(String.format(",%s,", childAreaRely.getName()));
        }

        // 处理完子节点逻辑后，修改其所有子节点路径
        List<AreaRely> subChildAreaRelyList = areaRelyRepository.findAllByPathRegex(childAreaRely.getName());
        subChildAreaRelyList.forEach(areaRely -> {
            areaRely.setPath(String.format("%s%s", childAreaRely.getPath(),
                    StringUtils.substringAfter(areaRely.getPath(), childAreaRely.getName() + ",")));
        });
        areaRelyRepository.saveAll(subChildAreaRelyList);

        return areaRelyRepository.save(childAreaRely);
    }

    /**
     * 判断树中是否存在父节点Id
     *
     * @param tree     {@link AreaRelyTreeVO}
     * @param parentId 父节点Id
     */
    private void containsChildTree(AreaRelyTreeVO tree, String parentId) {
        if (tree != null) {
            if (tree.getId().equals(parentId)) {
                throw new CloudEasyException(ResultCodeEnum.FORBID_UPDATE);
            }
            List<AreaRelyTreeVO> childAreaRelyTreeVOS = tree.getChildAreaRelyTreeVOS();
            if (childAreaRelyTreeVOS != null) {
                for (AreaRelyTreeVO areaRelyTreeVO : childAreaRelyTreeVOS) {
                    containsChildTree(areaRelyTreeVO, parentId);
                }
            }
        }
    }

    /**
     * 获取节点信息
     *
     * @param areaId 区域Id
     * @return {@link AreaRely}
     */
    private AreaRely getAreaRely(String areaId) {
        AreaRely areaRely = null;
        // 首先查看子节点是否属于存在
        Optional<AreaRely> areaRelyOptional = areaRelyRepository.findById(areaId);
        if (!areaRelyOptional.isPresent()) {
            // 获取子节点区域信息
            Optional<Area> areaOptional = areaRepository.findById(areaId);
            if (!areaOptional.isPresent()) {
                throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
            }
            Area area = areaOptional.get();
            areaRely = new AreaRely();
            BeanUtils.copyProperties(area, areaRely);
        } else {
            areaRely = areaRelyOptional.get();
        }
        return areaRely;
    }

    /**
     * 获取区域节点树
     *
     * @param id Id
     * @return {@link AreaRelyTreeVO}
     */
    public AreaRelyTreeVO tree(String id) {
        Optional<Area> areaOptional = areaRepository.findById(id);
        if (!areaOptional.isPresent()) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        List<AreaRely> areaRelyList = areaRelyRepository.findAllByPathRegex(String.format("%s", areaOptional.get().getName()));
        // 转换为以name为key的Map
        Map<String, AreaRely> idAreaRelyMap = areaRelyList.stream().collect(Collectors.toMap(AreaRely::getId, areaRely -> areaRely, (o, o2) -> o));
        // 获取根节点
        AreaRely root = idAreaRelyMap.get(id);
        return compose(root, idAreaRelyMap);
    }

    /**
     * 递归组合节点树
     *
     * @param root        根节点
     * @param areaRelyMap 以Id为key的{@link AreaRely}Map
     * @return {@link AreaRelyTreeVO}
     */
    public AreaRelyTreeVO compose(AreaRely root, Map<String, AreaRely> areaRelyMap) {
        if (root == null) {
            return null;
        }
        // 构建当前节点对象
        AreaRelyTreeVO rootVO = AreaRelyTreeVO.builder().id(root.getId()).name(root.getName()).build();
        // 获取子节点Id列表
        List<String> childIds = root.getChildIds();

        List<AreaRelyTreeVO> areaRelyTreeVOS = new ArrayList<>();
        if (childIds != null && childIds.size() > 0) {
            // 遍历子节点Id
            for (String childId : childIds) {
                // 获取子节点
                AreaRely child = areaRelyMap.get(childId);
                // 递归装配
                AreaRelyTreeVO childVO = compose(child, areaRelyMap);
                if (childVO != null) {
                    areaRelyTreeVOS.add(childVO);
                }
            }
        }
        // 将子区域列表添加到当前节点中
        if (areaRelyTreeVOS.size() > 0) {
            rootVO.setChildAreaRelyTreeVOS(areaRelyTreeVOS);
        }
        // 返回当前节点
        return rootVO;
    }

    /**
     * 删除节点
     * 子节点将归属于当前节点的父节点
     *
     * @param id Id
     */
    public void delete(String id) {
        // 获取节点
        Optional<AreaRely> areaRelyOptional = areaRelyRepository.findById(id);
        if (!areaRelyOptional.isPresent()) {
            return;
        }
        AreaRely areaRely = areaRelyOptional.get();
        String parentName = AreaPathUtil.parseParent(areaRely.getPath());
        // 获取所有子节点
        List<AreaRely> childAreaRelyList = areaRelyRepository.findAllByPathRegex(areaRely.getName());
        childAreaRelyList.remove(areaRely);

        // 如果父节点存在
        if (parentName != null) {
            Optional<AreaRely> parentAreaRelyOptional = areaRelyRepository.findByNameLike(parentName);
            if (parentAreaRelyOptional.isPresent()) {
                AreaRely parentAreaRely = parentAreaRelyOptional.get();
                // 删除掉当前子节点
                parentAreaRely.getChildIds().remove(areaRely.getId());
                // 接收当前子节点的所有子节点信息
                if (areaRely.getChildIds() != null) {
                    parentAreaRely.getChildIds().addAll(areaRely.getChildIds());
                }
                areaRelyRepository.save(parentAreaRely);
            }
        } else {
            parentName = AreaConstant.GLOBAL_ROOT_AREA_NAME;
        }
        // 通知修改用户管理区域u
        ChangeOperateAreaVO changeOperateAreaVO = ChangeOperateAreaVO.builder()
                .rawAreaName(areaRely.getName())
                .targetAreaName(parentName)
                .build();
        amqpTemplate.convertAndSend(AmqpConstant.UserConstant.USER_CHANGE_AREA_ROUTING_KEY, JsonUtil.toJson(changeOperateAreaVO));

        // 由于是归属于父区域，所以所有的子节点直接替换掉当前区域即可
        childAreaRelyList.forEach(childAreaRely -> {
            // 所有子节点
            childAreaRely.setPath(childAreaRely.getPath().replace(String.format("%s,", areaRely.getName()), ""));
        });
        areaRelyRepository.delete(areaRely);
        areaRelyRepository.saveAll(childAreaRelyList);
    }

    /**
     * 判断父区域列表是否可以包含所有子区域列表
     *
     * @param parentNames 父区域名称列表
     * @param childNames  子区域名称列表
     * @return {@link Boolean}
     */
    public Boolean isSubArea(List<String> parentNames, List<String> childNames) {
        List<String> parentPaths = areaRelyRepository.findAllByNameIn(parentNames).stream().map(AreaRely::getPath).collect(Collectors.toList());
        List<String> childPaths = areaRelyRepository.findAllByNameIn(childNames).stream().map(AreaRely::getPath).collect(Collectors.toList());
        return AreaPathUtil.contains(parentPaths, childPaths);
    }

    /**
     * 判断是否可以操作当前区域
     *
     * @param permissionId  权限Id
     * @param targetAreas   目标区域
     * @param operateUserId 操作用户Id
     * @return {@link Boolean}
     */
    public Boolean canDo(String permissionId, List<String> targetAreas, String operateUserId) {
        Result<List<UserRoleVO>> result = userClient.operateArea(permissionId, operateUserId);
        if (!result.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
            throw new CloudEasyException(ResultCodeEnum.REMOTE_ERROR);
        }
        List<String> childPaths = areaRelyRepository.findAllByNameIn(targetAreas).stream().map(AreaRely::getPath).collect(Collectors.toList());
        List<UserRoleVO> userRoleVOS = result.getData();
        List<String> parentPaths = userRoleVOS.stream().map(userRoleVO -> {
            Optional<AreaRely> areaRelyOptional = areaRelyRepository.findByNameLike(userRoleVO.getOperateArea());
            if (areaRelyOptional.isPresent()) {
                AreaRely areaRely = areaRelyOptional.get();
                if (!userRoleVO.getWithSelf()) {
                    return StringUtils.substringAfter(areaRely.getPath(), areaRely.getName());
                }
                return areaRely.getPath();
            }
            return null;
        }).collect(Collectors.toList());
        return AreaPathUtil.contains(parentPaths, childPaths);
    }

    /**
     * 初始化树
     */
    public void init() {
        AreaRely areaRely = AreaRely.builder()
                .id(AreaConstant.GLOBAL_ROOT_AREA_ID)
                .name(AreaConstant.GLOBAL_ROOT_AREA_NAME)
                .path(",root,")
                .build();
        areaRelyRepository.save(areaRely);
    }
}
